package com.xuelang.pipeline.app.pipeline.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.xuelang.hiforce.base.tracing.TracingUtils;
import com.xuelang.jadp.client.sys.dto.UserInfoDTO;
import com.xuelang.pipeline.app.pipeline.PipelineTemplateService;
import com.xuelang.pipeline.app.pipeline.converter.PipelineTemplateDTOToPipelineTemplateConverter;
import com.xuelang.pipeline.app.pipeline.converter.VariableSettingToDTOConverter;
import com.xuelang.pipeline.app.task.converter.PipelineConfigTaskDTOToTaskConverter;
import com.xuelang.pipeline.client.common.constant.Constant;
import com.xuelang.pipeline.client.common.constant.DictConstant;
import com.xuelang.pipeline.client.common.request.PageParam;
import com.xuelang.pipeline.client.common.result.PageRecord;
import com.xuelang.pipeline.client.common.util.BizAssert;
import com.xuelang.pipeline.client.pipeline.dto.PipelineTemplateConfigDTO;
import com.xuelang.pipeline.client.pipeline.dto.PipelineTemplateDTO;
import com.xuelang.pipeline.client.pipeline.dto.PipelineTemplateQuery;
import com.xuelang.pipeline.client.pipeline.dto.VariableSettingDTO;
import com.xuelang.pipeline.client.resource.dto.DictQuery;
import com.xuelang.pipeline.client.task.dto.PipelineConfigTaskDTO;
import com.xuelang.pipeline.client.task.dto.TaskQuery;
import com.xuelang.pipeline.domain.pipeline.PipelineTemplate;
import com.xuelang.pipeline.domain.pipeline.VariableSetting;
import com.xuelang.pipeline.domain.pipeline.service.PipelineTemplateDomainService;
import com.xuelang.pipeline.domain.pipeline.service.VariableSettingDomainService;
import com.xuelang.pipeline.domain.resource.Dict;
import com.xuelang.pipeline.domain.resource.service.DictDomainService;
import com.xuelang.pipeline.domain.task.Task;
import com.xuelang.pipeline.domain.task.service.TaskDefDomainService;
import com.xuelang.pipeline.domain.task.service.TaskDomainService;
import com.xuelang.sys.app.sys.UserManagerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 流水线模板服务实现类
 *
 * @author xiwen.zxg
 * @since 2024/5/15
 */
@Service
public class PipelineTemplateServiceImpl implements PipelineTemplateService {

    @Autowired
    PipelineTemplateDomainService pipelineTemplateDomainService;
    @Autowired
    VariableSettingDomainService variableSettingDomainService;
    @Autowired
    TaskDomainService taskDomainService;
    @Autowired
    TaskDefDomainService taskDefDomainService;
    @Autowired
    DictDomainService dictDomainService;
    @Autowired
    UserManagerService userManagerService;

    private PipelineTemplateDTO fullfillCreator(PipelineTemplateDTO pipelineTemplateDTO){
        String creatorId = pipelineTemplateDTO.getCreatorId();
        UserInfoDTO user = userManagerService.getUserById(creatorId);
        if (Objects.nonNull(user)) {
            pipelineTemplateDTO.setCreator(user.getRealName());
        }else{
            pipelineTemplateDTO.setCreator(Constant.UNKNOWN_USER);
        }
        return pipelineTemplateDTO;
    }

    @Override
    public PageRecord<PipelineTemplateDTO> pageList(PageParam<PipelineTemplateQuery> dtoPageParam) {
        PipelineTemplateQuery condition = dtoPageParam.getCondition();
        condition = condition == null ? new PipelineTemplateQuery() : condition;
        PageRecord<PipelineTemplate> pageRecord = pipelineTemplateDomainService.pageList(
                dtoPageParam.getCurrentPage(),
                dtoPageParam.getPageSize(),
                condition);
        return new PageRecord<>(
                pageRecord.getCurrentPage(),
                pageRecord.getPageSize(),
                pageRecord.getTotalCount(),
                pageRecord.getTotalPages(),
                pageRecord.getRecords().stream()
                        .map(PipelineTemplateDTOToPipelineTemplateConverter.INSTANCE::pipelineTemplateToPipelineTemplateDTO)
                        .map(this::fullfillCreator)
                        .collect(Collectors.toList()));
    }

    @Override
    public List<PipelineTemplateDTO> list(PipelineTemplateQuery pipelineTemplateQuery) {
        return pipelineTemplateDomainService.list(pipelineTemplateQuery).stream()
                .map(PipelineTemplateDTOToPipelineTemplateConverter.INSTANCE::pipelineTemplateToPipelineTemplateDTO)
                .map(this::fullfillCreator)
                .collect(Collectors.toList());
    }
    private String getUserId() {
        String getUserId = TracingUtils.getUserId();
        return StrUtil.isBlank(getUserId) ? "-1" : getUserId;
    }
    @Override
    public Boolean add(PipelineTemplateConfigDTO pipelineTemplateConfigDTO) {
        PipelineTemplateDTO pipelineTemplateDTO = pipelineTemplateConfigDTO.getBaseInfo();
        BizAssert.notNull(pipelineTemplateDTO.getName(), "name不能为空");
        BizAssert.notNull(pipelineTemplateDTO.getComponentType(), "componentType不能为空");
        BizAssert.notNull(pipelineTemplateDTO.getProgramLanguage(), "programLanguage不能为空");
        PipelineTemplate pipelineTemplate = PipelineTemplateDTOToPipelineTemplateConverter.INSTANCE.pipelineTemplateDTOToPipelineTemplate(pipelineTemplateDTO);
        String pipelineTemplateCode = IdUtil.fastSimpleUUID();
        pipelineTemplate.setUuid(pipelineTemplateCode);
        pipelineTemplate.setCreatorId(getUserId());
        pipelineTemplate.setUpdaterId(getUserId());
        List<PipelineConfigTaskDTO> pipelineConfigTaskDTOList = pipelineTemplateConfigDTO.getTaskList();
        List<VariableSettingDTO> variableSettingList = pipelineTemplateConfigDTO.getVariableSettingList();
        if (CollUtil.isNotEmpty(pipelineConfigTaskDTOList)) {
            // 加入流水线模板关联的流水线模板任务
            pipelineConfigTaskDTOList.forEach(pipelineConfigTaskDTO -> {
                pipelineConfigTaskDTO.setPipelineCode(pipelineTemplateCode);
                pipelineConfigTaskDTO.setCreatorId(getUserId());
                pipelineConfigTaskDTO.setUpdaterId(getUserId());
                taskDomainService.save(PipelineConfigTaskDTOToTaskConverter.INSTANCE.pipelineConfigTaskDTOToTask(pipelineConfigTaskDTO));
            });
        }
        if (CollUtil.isNotEmpty(variableSettingList)) {
            List<VariableSetting> settingList = variableSettingList.stream()
                    .map(VariableSettingToDTOConverter.INSTANCE::fromDTO)
                    .peek(variableSetting -> {
                        variableSetting.setPipelineCode(pipelineTemplateCode);
                        variableSetting.setCreatorId(getUserId());
                        variableSetting.setUpdaterId(getUserId());
                    })
                    .collect(Collectors.toList());
            variableSettingDomainService.addBatch(settingList, TracingUtils.getUserId(), new Date());
        }
        return pipelineTemplateDomainService.save(pipelineTemplate);
    }

    @Override
    public Boolean update(PipelineTemplateConfigDTO pipelineTemplateConfigDTO) {
        PipelineTemplateDTO pipelineTemplateDTO = pipelineTemplateConfigDTO.getBaseInfo();
        BizAssert.notNull(pipelineTemplateDTO.getUuid(), "uuid不能为空");
        BizAssert.notNull(pipelineTemplateDTO.getName(), "name不能为空");
        BizAssert.notNull(pipelineTemplateDTO.getComponentType(), "componentType不能为空");
        BizAssert.notNull(pipelineTemplateDTO.getProgramLanguage(), "programLanguage不能为空");
        PipelineTemplate pipelineTemplate = PipelineTemplateDTOToPipelineTemplateConverter.INSTANCE.pipelineTemplateDTOToPipelineTemplate(pipelineTemplateDTO);
        List<PipelineConfigTaskDTO> pipelineConfigTaskDTOList = pipelineTemplateConfigDTO.getTaskList();
        List<VariableSettingDTO> variableSettingList = pipelineTemplateConfigDTO.getVariableSettingList();
        // 先删除掉原流水模板UUID-流水线编码关联的流水线任务信息
        taskDomainService.deleteByPipelineCode(pipelineTemplateDTO.getUuid());
        if (CollUtil.isNotEmpty(pipelineConfigTaskDTOList)) {
            // 重新加入新关联的流水线任务
            pipelineConfigTaskDTOList.forEach(pipelineConfigTaskDTO -> {
                pipelineConfigTaskDTO.setId(null);
                pipelineConfigTaskDTO.setPipelineCode(pipelineTemplateDTO.getUuid());
                pipelineConfigTaskDTO.setUpdaterId(TracingUtils.getUserId());
                pipelineConfigTaskDTO.setCreatorId(TracingUtils.getUserId());
                taskDomainService.save(PipelineConfigTaskDTOToTaskConverter.INSTANCE.pipelineConfigTaskDTOToTask(pipelineConfigTaskDTO));
            });
        }
        if (CollUtil.isNotEmpty(variableSettingList)) {
            List<VariableSetting> settingList = variableSettingList.stream()
                    // 排除掉通用变量，是否通用变量，1是0否
                    .filter(variableSettingDTO -> variableSettingDTO.getIsCommon() != 1)
                    .map(VariableSettingToDTOConverter.INSTANCE::fromDTO)
                    .peek(variableSetting -> variableSetting.setPipelineCode(pipelineTemplateDTO.getUuid()))
                    .collect(Collectors.toList());
            // 判断该流水线是否已存在变量配置
            List<VariableSetting> currentSettingList = variableSettingDomainService.queryByPipelineCode(pipelineTemplateDTO.getUuid());
            if (CollectionUtils.isNotEmpty(currentSettingList)) {
                // 获取当前存在的变量ID列表
                List<Long> currentIds = currentSettingList.stream()
                        .peek(variableSetting -> variableSetting.setPipelineCode(pipelineTemplateDTO.getUuid()))
                        .map(VariableSetting::getId)
                        .collect(Collectors.toList());

                // 获取新变量的ID列表
                List<Long> newIds = settingList.stream()
                        .map(VariableSetting::getId)
                        .collect(Collectors.toList());

                // 找出在此次未出现的变量ID列表
                currentIds.removeAll(newIds);
                if (CollUtil.isNotEmpty(currentIds)) {
                    // 删除未出现的变量
                    variableSettingDomainService.removeByIds(currentIds);
                }
            }

            // 拆分为需要新增和需要更新的列表
            List<VariableSetting> settingsToAdd = settingList.stream()
                    .filter(setting -> setting.getId() == null)
                    .collect(Collectors.toList());

            List<VariableSetting> settingsToUpdate = settingList.stream()
                    .filter(setting -> setting.getId() != null)
                    .collect(Collectors.toList());

            // 批量新增和更新
            if (CollUtil.isNotEmpty(settingsToAdd)) {
                variableSettingDomainService.addBatch(settingsToAdd, TracingUtils.getUserId(), new Date());
            }
            if (CollUtil.isNotEmpty(settingsToUpdate)) {
                variableSettingDomainService.updateBatch(settingsToUpdate, TracingUtils.getUserId(), new Date());
            }
        }
        return pipelineTemplateDomainService.update(pipelineTemplate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(String pipelineTemplateUuid) {
        BizAssert.notNull(pipelineTemplateUuid, "pipelineTemplateUuid不能为空");
        // 删除流水线模板关联的流水线任务
        taskDomainService.deleteByPipelineCode(pipelineTemplateUuid);
        // 删除流水线模板关联的流水线变量设置
        variableSettingDomainService.removeByCodes(Collections.singletonList(pipelineTemplateUuid));
        // 删除流水线模板
        return pipelineTemplateDomainService.delete(pipelineTemplateUuid);
    }

    @Override
    public PipelineTemplateDTO query(String pipelineTemplateUuid) {
        BizAssert.notNull(pipelineTemplateUuid, "pipelineTemplateUuid不能为空");
        return PipelineTemplateDTOToPipelineTemplateConverter.INSTANCE.pipelineTemplateToPipelineTemplateDTO(pipelineTemplateDomainService.query(pipelineTemplateUuid));
    }

    @Override
    public Boolean copyPipelineTemplate(String pipelineTemplateUuid, String newPipelineTemplateName) {
        BizAssert.notEmpty(pipelineTemplateUuid, "将复制的流水线模板唯一标识不能为空");
        BizAssert.notEmpty(newPipelineTemplateName, "流水线模板名称不能为空");
        PipelineTemplateConfigDTO fromPipelineTemplateConfigDTO = this.pipelineTemplateConfig(pipelineTemplateUuid);
        BizAssert.notNull(fromPipelineTemplateConfigDTO, "未查询到对应流水线模板信息");
        // 重置流水线基础信息
        PipelineTemplateDTO baseInfo = fromPipelineTemplateConfigDTO.getBaseInfo();
        baseInfo.setName(newPipelineTemplateName);
        baseInfo.setCreatorId(null);
        baseInfo.setCreateTime(null);
        // 重置流程编排数据
        List<PipelineConfigTaskDTO> taskList = fromPipelineTemplateConfigDTO.getTaskList();
        if (CollUtil.isNotEmpty(taskList)) {
            taskList.forEach(task -> {
                task.setId(null);
                task.setPipelineCode(null);
                task.setUpdaterId(TracingUtils.getUserId());
                task.setCreatorId(TracingUtils.getUserId());
                task.setUpdateTime(null);
                task.setCreateTime(null);
            });
        }
        // 重置变量配置数据
        List<VariableSettingDTO> variableSettingList = fromPipelineTemplateConfigDTO.getVariableSettingList();
        if (CollUtil.isNotEmpty(variableSettingList)) {
            variableSettingList = variableSettingList.stream().filter(variableSetting -> variableSetting.getIsCommon() != 1).collect(Collectors.toList());
            variableSettingList.forEach(variableSetting -> {
                variableSetting.setId(null);
                variableSetting.setPipelineCode(null);
                variableSetting.setUpdaterId(TracingUtils.getUserId());
                variableSetting.setCreatorId(TracingUtils.getUserId());
                variableSetting.setUpdateTime(null);
                variableSetting.setCreateTime(null);
            });
            fromPipelineTemplateConfigDTO.setVariableSettingList(variableSettingList);
        }
        return this.add(fromPipelineTemplateConfigDTO);
    }

    @Override
    public PipelineTemplateConfigDTO pipelineTemplateConfig(String pipelineTemplateUuid) {
        BizAssert.notEmpty(pipelineTemplateUuid, "pipelineTemplateUuid不能为空.");
        PipelineTemplate pipelineTemplate = pipelineTemplateDomainService.query(pipelineTemplateUuid);
        BizAssert.notNull(pipelineTemplate, "未查询到对应流水线模板信息.");
        PipelineTemplateDTO pipelineTemplateDTO = PipelineTemplateDTOToPipelineTemplateConverter.INSTANCE.pipelineTemplateToPipelineTemplateDTO(pipelineTemplate);
        List<Task> taskList = taskDomainService.list(TaskQuery.builder().pipelineCode(pipelineTemplateUuid).build());
        List<PipelineConfigTaskDTO> pipelineConfigTaskDTOList = new ArrayList<>();
        if (CollUtil.isNotEmpty(taskList)) {
            pipelineConfigTaskDTOList = taskList.stream().map(PipelineConfigTaskDTOToTaskConverter.INSTANCE::taskToPipelineConfigTaskDTO).collect(Collectors.toList());
            pipelineConfigTaskDTOList.forEach(taskDTO -> {
                // 增加流水线任务定义schema信息
                Optional.ofNullable(taskDefDomainService.query(taskDTO.getTaskDefUuid()))
                        .ifPresent(taskDef -> taskDTO.setSchema(taskDef.getSchemaConfig()));
            });
        }
        // 查询流水线变量设置
        List<VariableSettingDTO> variableSettingList = Optional.ofNullable(variableSettingDomainService.queryByPipelineCode(pipelineTemplateUuid)).orElse(new ArrayList<>()).stream()
                .map(VariableSettingToDTOConverter.INSTANCE::toDTO).collect(Collectors.toList());
        // 增加通用变量部分数据，仿在列表头部
        List<Dict> pipelineCommonVariableList = dictDomainService.list(DictQuery.builder().dictType(DictConstant.DICT_TYPE_PIPELINE_COMMON_VARIABLE).build());
        pipelineCommonVariableList.forEach(dict -> variableSettingList.add(
                0,
                VariableSettingDTO.builder()
                        .name(dict.getDictCode())
                        .defaultValue(dict.getDictValue())
                        .isRunSetting(0)
                        .isRunSelect(0)
                        .isPrivate(1)
                        .isCommon(1)
                        .build()
        ));
        return PipelineTemplateConfigDTO.builder()
                .baseInfo(pipelineTemplateDTO)
                .taskList(pipelineConfigTaskDTOList)
                .variableSettingList(variableSettingList)
                .build();
    }

}
